#ifndef PL8COMM_H
#define PL8COMM_H

#include "comm.h"
#include <ftdi.h>
#include <QtGui>

#define PL8C_ERROR_COULD_NOT_OPEN     (1)
#define PL8C_ERROR_WRONG_RETURN_SIZE  (2)
#define PL8C_ERROR_CRC_FAILED         (3)
#define PL8C_ERROR_UNAVAILABLE        (4)
#define PL8C_CHARGER_NOT_READY        (5)

#define OPR_START_CHARGE                 'C'
#define OPR_START_CHARGE_NO_BANANAS      'c'
#define OPR_START_DISCHARGE              'D'
#define OPR_START_DISCHARGE_NO_BANANAS   'd'
#define OPR_START_MONITOR                'M'
#define OPR_START_MONITOR_NO_BANANAS     'm'
#define OPR_START_CYCLE                  'Y'
#define OPR_START_CYCLE_NO_BANANAS       'y'
#define OPR_PRESS_ACKNOLEDGE             'E'

#define OPR_TRIES 3


//start charge/discharge/monitor/cycle:
//readRam();
//verify rpk.mode == 0
//chargerOperation(OPR_START_####);

//stop charge/discharge/monitor/cycle:
//readRam();
//verify rpk.mode == 6, 7, 8, 9 , 11
//chargerOperation(OPR_PRESS_ACKNOLEDGE);

//clear error:
//readRam();
//verify rpk.mode == 0
//chargerOperation(OPR_PRESS_ACKNOLEDGE);


class pl8Comm : public QObject
{
    Q_OBJECT

public:
    explicit pl8Comm(QObject * parent = 0);
    ~pl8Comm();

    void startPolling(int period);
    void stopPolling();
    void restartPolling();

    int selectPreset(quint8 p);
    int chargerOperation(char opr);
    int readPresets();

    bool chargerStart(char opr);
    bool chargerStop();
    bool chargerClearError();


    void getCellVolt(float * vals); // 8 * 16bit values
    void getCellInternalResistence(float * vals); // 8 * 16bit values
    void getBalancerPWM(quint8 * vals); // 8 * 8bit values
    quint32 getChargeSeconds();

    bool isSafetyCharge()                  { return (rpk.statusFlags & ST_SAFETY_CHARGE_BIT); }
    bool isChargeDischargeComplete()       { return (rpk.statusFlags & ST_CHG_DSCH_COMPLETE_BIT); }
    bool isReduceAmps()                    { return (rpk.statusFlags & ST_REDUCE_AMPS_BIT); }
    bool isNodeOnlyCharge()                { return (rpk.statusFlags & ST_NODE_ONLY_CHRG_BIT); }

    bool isDischargeRunning()              { return (rpk.rxStatusFlags & RXS_DSCH_RUNNING_BIT); }
    bool isRegenDischarge()                { return (rpk.rxStatusFlags & RXS_REGEN_DISCHARGE_BIT); }
    bool isChargeRunning()                 { return (rpk.rxStatusFlags & RXS_CHG_RUNNING_BIT); }
    bool isBalancersRunning()              { return (rpk.rxStatusFlags & RXS_BALANCERS_RUNNING_BIT); }

    bool isHighTemp()                      { return (rpk.status2Flags & ST2_HIGH_TEMP_BIT); }

    bool isPresetIsValid()                 { return (rpk.status6Flags & ST6_PRESET_IS_VALID_AND_RUNABLE_BIT); }
    bool isConstantVoltage()               { return (rpk.status6Flags & ST6_CONSTANT_VOLTAGE_BIT); }
    bool isRegenDischargeFailed()          { return (rpk.status6Flags & ST6_REGEN_DISCHARGE_FAILED_BIT); }

    quint8 getChargerID()                  { return rpk.chargerID+1; }
    float getFwVersion()                   { return ((float)rpk.fwVersion) / 100; }
    quint16 getSyncPWMDrive()              { return rpk.syncPWMDrive; }
    float getChargeCurrentSetPoint()       { return ((float)rpk.chargeCurrentSetPoint) / 1666; }
    float getSupplyVoltsWCurrent()         { return rpk.supplyVoltsWCurrent * I2V / 16; }
    float getSupplyVolts()                 { return rpk.supplyVolts * I2V; }
    float getCPUTemp()                     { return (2.5 * ((float)rpk.cpuTemp) / 4095 - 0.986) / 0.00355; }
    float getFastApmsReading()             { return ((float)rpk.fastApmsReading) / 600; }
    float getOutputPositiveReading()       { return rpk.outputPositiveReading * I2V; }
    float getMahInToBattery()              { return ((float)rpk.mahInToBattery) / 2160; }
    float getMahOutOfBattery()             { return ((float)rpk.mahOutOfBattery) / 2160; }
    float getAavgFuel()                    { return ((float)rpk.avgFuel) / 10; }
    float getStartFuel()                   { return ((float)rpk.startFuel) / 10; }
    float getAvgAmpsReading()              { return ((float)rpk.avgAmpsReading) / 600; }
    float getVRAmps()                      { return ((float)rpk.vrAmps) / 600; }
    float getNiCdFallbackVolts()           { return rpk.nicdFallbackVolts * I2V - getMaxCellVolts(); }
    float getMaxCellVolts()                { return rpk.maxCellVolts * I2V; }
    float getSupplyAmps()                  { return ((float)rpk.supplyAmps) / 150; }
    float getBatteryPositive()             { return rpk.batteryPositive * I2V; }
    float getBatteryNegative()             { return rpk.batteryNegative * I2V; }
    float getRegenVoltSetPoint()           { return rpk.regenVoltSetPoint * I2V; }
    float getDischargeSetAmps()            { return ((float)rpk.dischargeSetAmps) / 600; }
    quint16 getInternalDischargePWM()      { return rpk.internalDischargePWM; }
    float getNegativeNodeVoltDrop()        { return rpk.negativeNodeVoltDrop * I2V; }
    float getPositiveNodeVoltDrop()        { return rpk.positiveNodeVoltDrop * I2V; }
    float getStartSupplyVolts()            { return rpk.startSupplyVolts * I2V; }
    float getVROffset()                    { return ((float)rpk.vrOffset) / 6.3984; }
    float getSlowAvgAmps()                 { return ((float)rpk.slowAvgAmps) / 600; }
    float getPresetSetChargeAmps()         { return ((float)rpk.presetSetChargeAmps) / 600; }
    quint8 getDetectedCellCount()          { return rpk.detectedCellCount; }
    quint8 getMode()                       { return rpk.mode; }
    quint8 getErrorCode()                  { return rpk.errorCode; }
    quint8 getChemistry()                  { return rpk.chemistry; }
    quint8 getLoadedPresetNumber()         { return rpk.loadedPresetNumber; }
    quint8 getScreenNumber()               { return rpk.screenNumber; }
    quint8 getCycleNumber()                { return rpk.cycleNumber; }
    quint8 getPowerReducedReason()         { return rpk.powerReducedReason; }

    Preset getPreset(int i)                { return presets[i]; }

    QString getModeName();
    QString getChemistryName();
    QString getPresetName(int presetNum);
    QString getPowerReducedReasonName();


    int readRam();


signals:
    void chargeDischargeComplete();
    void highTemp();
    void ramDataAvailable();
    void requestConfirmation();
    void chargerError(int err);


private slots:
    void timerEvent();

private:
    RamPacket rpk;
    Preset presets[25];

    int do3reads(); // tries the operation OPR_TRIES times
    int do3operations(char opr); // tries the operation OPR_TRIES times

    QTimer timer;

    struct ftdi_context ftdic;

    bool ftdiOpen();
    bool ftdiClose();
    void flipMSB();
    void copyToPreset(quint8 * buf, int presetNum);

    quint16 calcCRC(quint8 * gdata, int len, int crcInit);
    void clearError();
    void resetFlags();

    bool chargeDischargeCompleteFired;
    bool highTempFired;
};

#endif // PL8COMM_H
